import random
from dataclasses import dataclass
from typing import List

import numpy



@dataclass
class Quantiles:

    reverse:bool = False

    def __post_init__(self):
        self._values:numpy.ndarray = None # 对应值
        self._quantile_list:numpy.ndarray = None   # 占比

    @property
    def mean(self):
        return self._values.mean()

    def __str__(self):
        return f"[" \
               f"10:{self.get_value(0.1):.1f}," \
               f"30:{self.get_value(0.3):.1f}," \
               f"50:{self.get_value(0.5):.1f}," \
               f"70:{self.get_value(0.7):.1f}," \
               f"90:{self.get_value(0.9):.1f}," \
               f"]"

    def get_quantile(self,value:float):
        """
        返回所在占比值。
        """
        size = len(self._values)
        assert size > 0
        if size == 1:
            return 1
        if value <= self._values[0]:
            return 0
        elif value >= self._values[-1]:
            return 1
        index = -1
        for i in range(1, size):
            if self._values[i - 1] < value and value <= self._values[i]:
                index = i
                break

        assert index != -1
        ### 在index -1 和 index 之间。
        return self._quantile_list[index - 1] + (self._quantile_list[index] - self._quantile_list[index - 1]) \
               * (value - self._values[index - 1]) / (
                           self._values[index] - self._values[index - 1])


    def get_value(self,quantile:float):
        """
        返回quantile占比对应的值。
        param
            quantile 在（0,1）之间。
        """
        if quantile <= 0:
            raise RuntimeError("quantile不能为0")
        if quantile > 1:
            raise RuntimeError("quantile不能为大于1")

        size = len(self._quantile_list)
        assert size > 0
        if size == 1:
            return self._values[0]
        if quantile <= self._quantile_list[0]:
            return self._values[0]
        index = -1
        for i in range(1,size):
            if self._quantile_list[i-1] < quantile and  quantile <= self._quantile_list[i]:
                index = i
                break
        if index == -1:
            return  self._values[-1]
        ### 在index -1 和 index 之间。
        return self._values[index - 1] + (self._values[index] - self._values[index-1])\
               * (quantile - self._quantile_list[index-1]) / ( self._quantile_list[index] - self._quantile_list[index-1])


    @staticmethod
    def create(value_list:list,reverse = False):
        values = sorted(value_list, reverse=reverse)
        SIZE = len(values)
        quantile_list = numpy.full(SIZE, numpy.nan)
        if SIZE > 0:
            BASE =  SIZE + 1
            for i in range(0, SIZE):
                cur_probal = (i + 1) / BASE
                quantile_list[i] = numpy.float32(cur_probal)
        q = Quantiles()
        q._values = numpy.array(values)
        q._quantile_list = quantile_list
        assert len(quantile_list) == SIZE
        q.reverse = reverse
        return q

    def calc_factor(self, value:float, low_percent: float = 0.05, high_percent: float = 0.95):
        """
        计算生成0-1的因子值。low_percent以下和 high_percent以上作为误差值，不考虑
        """
        low_value = self.get_value(low_percent)
        high_value = self.get_value(high_percent)
        if value <= low_value:
            return 0
        elif value >= high_value:
            return 1
        return  (value - low_value ) / (high_value - low_value)



if __name__ == "__main__":
    from emi.util.statistics import BoxPlot,BoxPlot20
    from emi.util import NumUtils

    values = [15, 64, 23, 16, 32, 31, 37, 65, 10, 9, 10, 48, 25, 20]
    bp = BoxPlot.of_values(values)
    qtile = Quantiles.create(values)

    #assert NumUtils.is_equal(qtile.get_value(0.05),bp.min_limit)
    assert NumUtils.is_equal(qtile.get_value(0.25),bp.q1)
    assert NumUtils.is_equal(qtile.get_value(0.5),bp.median)
    assert NumUtils.is_equal(qtile.get_value(0.75),bp.q3)
    assert NumUtils.is_equal(qtile.get_value(0.95),bp.max_limit)

    values = numpy.random.uniform(low=-1000, high=1000, size=120)  ##随机生成涨幅情况

    box_plot = BoxPlot20.of_values(values)

    print(f"{box_plot}")

    #
    #
    # for i in range(0,100):
    #     values = numpy.random.uniform(low=-1000, high=1000, size=5000)  ##随机生成涨幅情况
    #     qtile = Quantiles.create(values)
    #     print(f"{i}")
    #     bp = BoxPlot20.of_values(values)
    #     assert NumUtils.is_equal(qtile.get_value(0.25), bp.q_25,0.01)
    #     assert NumUtils.is_equal(qtile.get_value(0.5), bp.q_50,0.01)
    #     assert NumUtils.is_equal(qtile.get_value(0.75), bp.q_75,0.01)
    #     assert NumUtils.is_equal(qtile.get_value(0.95), bp.q_95,1)





